﻿using System;
using System.ServiceModel;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.ServiceModel.Description;
using System.Threading;

namespace SimpleP2P
{
    [Guid("C337AB85-2B82-48A2-81F2-A4811A7EC01A")]
    public class SimpleP2PClient : ISimpleP2PClient
    {
        Peer peer = null;
        Thread peerThread = null;

        public int Start(string peerId, IntPtr hNotifyWnd)
        {
            Debug.Assert(hNotifyWnd != IntPtr.Zero);
            peer = new Peer(peerId, hNotifyWnd);
            peerThread = new Thread(peer.Run) { IsBackground = true };
            peerThread.Start();

            //wait for the server to start up.
            Thread.Sleep(1000);

            return 1;
        }

        public void CaptureStart()
        {
            peer.Channel.CaptureStart(peer.Id);
        }

        public void CaptureStop()
        {
            peer.Channel.CaptureStop(peer.Id);
        }

        public void CaptureImage()
        {
            peer.Channel.CaptureImage(peer.Id);
        }

        public void SetCurRunTime(int curRunTime)
        {
            peer.Channel.SetCurRunTime(peer.Id, curRunTime);
        }

        public void SetAviIndex(int aviIndex)
        {
            peer.Channel.SetAviIndex(peer.Id, aviIndex);
        }

        public void SetJpgIndex(int jpgIndex)
        {
            peer.Channel.SetJpgIndex(peer.Id, jpgIndex);
        }

        public void Stop()
        {
            if (peer != null)
            {
                peer.Stop();
            }
            if (peerThread != null)
            {
                peerThread.Join();
            }
        }
    }

    [Guid("22870FBF-D964-4179-8E41-4E0A3CA8848F")]
    public interface ISimpleP2PClient
    {
        int Start(string peerName, IntPtr hNotifyWnd);
        void CaptureStart();
        void CaptureStop();
        void CaptureImage();
        void SetCurRunTime(int curRunTime);
        void SetAviIndex(int aviIndex);
        void SetJpgIndex(int jpgIndex);
        void Stop();
    }

    //Contract for our network. It says we can 'ping'
    [ServiceContract(CallbackContract = typeof(IPing))]
    public interface IPing
    {
        [OperationContract(IsOneWay = true)]
        void CaptureStart(string sender);
        [OperationContract(IsOneWay = true)]
        void CaptureStop(string sender);
        [OperationContract(IsOneWay = true)]
        void CaptureImage(string sender);
        [OperationContract(IsOneWay = true)]
        void SetCurRunTime(string sender, int curRunTime);
        [OperationContract(IsOneWay = true)]
        void SetAviIndex(string sender, int aviIndex);
        [OperationContract(IsOneWay = true)]
        void SetJpgIndex(string sender, int jpgIndex);
    }

    //implementation of our ping class
    public class PingImplementation : IPing
    {
        [return: MarshalAs(UnmanagedType.Bool)]
        [DllImport("user32.dll", SetLastError = true)]
        static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool IsWindow(IntPtr hWnd);

        const int WM_APP = 0x8000;
        const int WM_P2PNotify = WM_APP + 2;

        const int P2PAction_CaptureStart = 0;
        const int P2PAction_CaptureStop = 1;
        const int P2PAction_CaptureImage = 2;
        const int P2PAction_SetCurRunTime = 3;
        const int P2PAction_SetAviIndex = 4;
        const int P2PAction_SetJpgIndex = 5;

        private IntPtr m_hNofityWnd = IntPtr.Zero;
        public PingImplementation(IntPtr hNofityWnd)
        {
            Debug.Assert(hNofityWnd != IntPtr.Zero);
            m_hNofityWnd = hNofityWnd;
        }

        public void CaptureStart(string sender)
        {
            Debug.Assert(m_hNofityWnd != IntPtr.Zero);
            if (string.Compare(sender, "wudong-notebook", true) == 0)
            {
                PostMessage(m_hNofityWnd, WM_P2PNotify, (IntPtr)P2PAction_CaptureStart, IntPtr.Zero);
            }
        }

        public void CaptureStop(string sender)
        {
            Debug.Assert(m_hNofityWnd != IntPtr.Zero);
            if (string.Compare(sender, "wudong-notebook", true) == 0)
            {
                PostMessage(m_hNofityWnd, WM_P2PNotify, (IntPtr)P2PAction_CaptureStop, IntPtr.Zero);
            }
        }

        public void CaptureImage(string sender)
        {
            Debug.Assert(m_hNofityWnd != IntPtr.Zero);
            if (string.Compare(sender, "wudong-notebook", true) == 0)
            {
                PostMessage(m_hNofityWnd, WM_P2PNotify, (IntPtr)P2PAction_CaptureImage, IntPtr.Zero);
            }
        }

        public void SetCurRunTime(string sender, int curRunTime)
        {
            Debug.Assert(m_hNofityWnd != IntPtr.Zero);
            if (string.Compare(sender, "wudong-notebook", true) == 0)
            {
                PostMessage(m_hNofityWnd, WM_P2PNotify, (IntPtr)P2PAction_SetCurRunTime, (IntPtr)curRunTime);
            }
        }

        public void SetAviIndex(string sender, int aviIndex)
        {
            Debug.Assert(m_hNofityWnd != IntPtr.Zero);
            if (string.Compare(sender, "wudong-notebook", true) == 0)
            {
                PostMessage(m_hNofityWnd, WM_P2PNotify, (IntPtr)P2PAction_SetAviIndex, (IntPtr)aviIndex);
            }
        }

        public void SetJpgIndex(string sender, int jpgIndex)
        {
            Debug.Assert(m_hNofityWnd != IntPtr.Zero);
            if (string.Compare(sender, "wudong-notebook", true) == 0)
            {
                PostMessage(m_hNofityWnd, WM_P2PNotify, (IntPtr)P2PAction_SetJpgIndex, (IntPtr)jpgIndex);
            }
        }
    }

    public class Peer
    {
        public string Id { get; private set; }

        private IntPtr m_hNotifyWnd = IntPtr.Zero;

        public IPing Channel;
        public IPing Host;

        public Peer(string id, IntPtr hNotifyWnd)
        {
            Debug.Assert(hNotifyWnd != IntPtr.Zero);
            Id = id;
            m_hNotifyWnd = hNotifyWnd;
        }

        private DuplexChannelFactory<IPing> _factory;
        public void StartService()
        {
            try
            {
                var binding = new NetPeerTcpBinding();
                binding.Security.Mode = SecurityMode.None;

                var endpoint = new ServiceEndpoint(
                    ContractDescription.GetContract(typeof(IPing)),
                    binding,
                    new EndpointAddress("net.p2p://SimpleP2P"));

                Host = new PingImplementation(m_hNotifyWnd);

                _factory = new DuplexChannelFactory<IPing>(
                    new InstanceContext(Host),
                    endpoint);

                var channel = _factory.CreateChannel();

                ((ICommunicationObject)channel).Open();

                // wait until after the channel is open to allow access.
                Channel = channel;
            }
            catch (Exception ex)
            {

                throw;
            }
        }

        public void StopService()
        {
            ((ICommunicationObject)Channel).Close();
            if (_factory != null)
                _factory.Close();
        }

        private readonly AutoResetEvent _stopFlag = new AutoResetEvent(false);
        public void Run()
        {
            //Console.WriteLine("[ Starting Service ]");
            StartService();

            //Console.WriteLine("[ Service Started ]");
            _stopFlag.WaitOne();

            //Console.WriteLine("[ Stopping Service ]");
            StopService();

            //Console.WriteLine("[ Service Stopped ]");
        }

        public void Stop()
        {
            _stopFlag.Set();
        }
    }
}
